2018 has been an exciting year for me to develop my skills in Python. I had so much exposure, not entirely from work, but drove by my sole interest in Python (Python 3 especially). We use Python 2 mainly at work, but I honestly spend more time on Python 3 for my personal development at home. So I get to have experiences in both Python 2 and 3.
Python 2 and 3 are, in some ways similar, but there are still fair bits of differences between the two, which is why there are compatibility libraries available like six or future where it helps you port your Python 2 code to Python 3. However, I have not had a big enough project to use these libraries on my own. I heard good things about them though.
Whenever I needed to do the switch from Python 2 to 3, I used Python 3’s built-in tool called, 2to3. From my experiences, I have not yet come across a situation where it did not handle well (not that I tried a million times though). I suspect the may have a hard time converting some complicated and sophisticated lines of codes. I suggest reviewing the codes thoroughly after the conversion.
NOTE: When I am comparing between Python 2 and 3, I am comparing the somewhat latest versions of both.
There are a fair amount of little things that had been improved/deprecated from Python 2 to 3. Even though I love most of the changes made in Python 3, some features are still missing in Python. The changes are robust, durable and well-thought which is why I started to enjoy coding in Python 3.
There are a few subtle changes to the import itself. The main difference, I would say, is that we no longer require __init__.py
to define our package. Yay!
Also, in Python 2, we would typically import like import xyz
, but in Python 3, it is preferred to use from . import xyz
syntax. We usually replace the dot
with the package location (folder and filename). It is not mandated, but rather a good coding habit.
Anyone who is somewhat familiar with Python would know this infamous change: print
is treated as a function call in Python 3.
Instead of print "foobar"
(in Python 2), we now call print as a function like this: print("foobar")
There is an option in Python 2 to enable using the print function like Python 3:
file=sys.stderr
is the way to pipe the output to standard error. For the comparison, in Python 2, we had to do something like this to pipe to stderr: print >> sys.stderr, "barbaz"
There aren’t any significant changes to how we raise exceptions. If you are familiar with Python 2 syntax, the syntax for raising exceptions should be pretty much the same. However, there was some extra work done on the catching side.
We used to catch exceptions like this in Python 2:
Here is the Python 3 with subtle changes to how it is caught:
The comma was replaced with “as,” which is more concise and descriptive.
There are a few changes to the integers in general. I want to focus on the division operator though. In Python 2, this is true:
It is pretty similar in Python 3, but float is by default instead of rounding down to an integer (even when an integer is used). I like this update as I couldn’t understand better why python would round down automatically. There were numerous times, I got burned by the miscalculation of divisions in Python. As you can see from the example below, you can use //
to mimic the Python 2 behaviour of round-down.
There have been a few radical revamp of Strings in Python 3. For the most critical part, Python 3 uses Unicode by default whereas Python 2 wasn’t.
Therefore, when porting, Python recommends prefixing the string with u''
on any existing Python 2 string like so, u'foobar'
. This way it is supported in both Python 2 and Python 3 without any broken characters. As you may have noticed, the letter u
refers to Unicode. You can optionally pass in below import in Python 2 to address Unicode issues.
This import takes care of having the u
prefix on every string. If you are worried about backward compatibility, you can be explicit to the string like b'foobar'
(used in Python 3) which would interpret the string as a “raw” byte string like Python 2.
You can contrarily think this way, unicode
strings in Python 2 are equivalent to str
in Python 3.
We were able to define classes like this which makes a distinct “class” type:
Then there is another way of defining a class:
We would have had to explicit inheritance from the “object.” Python 3 essentially inherit everything from the Object by default (like Java). Also, there were some changes in super calls, metaclasses, etc. I do not think it’s much helpful to show them here. You can search online easily for the changes if you are interested.
Python 3 dictionaries enforce the Ordering of inserts now (above 3.6). The change can help in situations when you need to retrieve items from insertion order. The change is not equal to “sorted keys” like how “TreeMap” works in Java. I found it a bit annoying not having the treemap in Python Standard Library (but what can I do, I think you can even import extra libraries if you want)
Also, there were some syntax changes on how we iterate dictionaries.
In Python 2, we do something like:
In Python 3, we no longer have the iter
methods and have a distinct call on each (keys, values, items) like: